#include "scope.h"

static bool g_scope_snap_buffer_ready = false;
static pingpong_t g_scope_snap_buffer_index = PING;

int scope_snap_buffer_dma_len(void)
{
    return (SCOPE_ADC_BUFFER_WORDS - dma_channel_hw_addr(g_scope_cfg.dma_chan_adc_data)->transfer_count - 1) * 2;
}

static uint32_t scope_snap_buffer_get_adc(int ch)
{
    int cur1 = scope_snap_buffer_dma_len();
    int cur2 = scope_snap_buffer_dma_len();

    while (cur1 == cur2) {
        cur1 = scope_snap_buffer_dma_len();
    }    

    if (ch == 0) {
        return g_scope_dma_points[PING][cur1].ch0;
    } else {
        return g_scope_dma_points[PING][cur1].ch1;
    }
}

uint32_t scope_snap_buffer_get_adc_avg(int ch, int samples)
{
    uint32_t sum = 0;

    for (int i = 0; i < samples; i++) {
        sum += scope_snap_buffer_get_adc(ch);
    }
    return sum / samples;
}

int scope_snap_buffer_get_data_offset(scope_snap_info *info)
{
    uint32_t average = 0;
    int start;
   
    for (int i = 0; i < HTTP_FS_IV_NUM * 2; i++) {
        if (g_scope_cfg.trigger_chnl == 0) {
            average += info->buffer[i].ch0_min;
        } else {
            average += info->buffer[i].ch1_min;
        }
    }
    average = average / HTTP_FS_IV_NUM / 2;
    start = HTTP_FS_IV_NUM / 2;
    for (int i = 0; i < HTTP_FS_IV_NUM; i++) {
        if (g_scope_cfg.trigger_chnl == 0) {
            if (g_scope_cfg.trigger_edge == SCOPE_EDGE_RISING) {
                if ((info->buffer[HTTP_FS_IV_NUM / 2 + i].ch0_min <= average) && (info->buffer[HTTP_FS_IV_NUM / 2 + i + 1].ch0_min >= average)) {
                    start = i;
                    break;
                }
            } else {
                if ((info->buffer[HTTP_FS_IV_NUM / 2 + i].ch0_min >= average) && (info->buffer[HTTP_FS_IV_NUM / 2 + i + 1].ch0_min <= average)) {
                    start = i;
                    break;
                }
            }
        } else {
            if (g_scope_cfg.trigger_edge == SCOPE_EDGE_RISING) {
                if ((info->buffer[HTTP_FS_IV_NUM / 2 + i].ch1_min <= average) && (info->buffer[HTTP_FS_IV_NUM / 2 + i + 1].ch1_min >= average)) {
                    start = i;
                    break;
                }
            } else {
                if ((info->buffer[HTTP_FS_IV_NUM / 2 + i].ch1_min >= average) && (info->buffer[HTTP_FS_IV_NUM / 2 + i + 1].ch1_min <= average)) {
                    start = i;
                    break;
                }
            }
        }
    }
    return start;
}

void scope_snap_buffer_move_data(scope_snap_info *info)
{
    int start;

    if (info->length != HTTP_FS_IV_NUM * 2) {
        return;
    }
    start = scope_snap_buffer_get_data_offset(info);
    for (int i = 0; i < HTTP_FS_IV_NUM; i++) {
        info->buffer[i].ch0_min = info->buffer[start + i].ch0_min;
        info->buffer[i].ch0_max = info->buffer[start + i].ch0_max;
        info->buffer[i].ch1_min = info->buffer[start + i].ch1_min;
        info->buffer[i].ch1_max = info->buffer[start + i].ch1_max;
    }
}

int scope_snap_buffer_copy_data(scope_snap_info *info)
{
    int step256 = scope_helper_get_step();
    scope_adc_data *pos;

    if (g_scope_snap_buffer_ready == false) {
        pos = g_scope_dma_points[PING];
        info->length = 1.0 * scope_snap_buffer_dma_len() / step256 * 256.0;
        // log_printf("scope_snap_buffer_copy_data: length = %d\n", info->length);
    } else {
        pos = g_scope_dma_points[!g_scope_snap_buffer_index];
        info->length = HTTP_FS_IV_NUM * 2;
    }
    if (info->length > HTTP_FS_IV_NUM * 2) {
        info->length = HTTP_FS_IV_NUM * 2;
    }

    for (int i = 0; i < info->length; i++) {
        int index = ((i * step256) >> 8) % (SCOPE_ADC_BUFFER_POINTS);

        info->buffer[i].ch0_min = pos[index].ch0;
        info->buffer[i].ch0_max = pos[index].ch0;
        info->buffer[i].ch1_min = pos[index].ch1;
        info->buffer[i].ch1_max = pos[index].ch1;
        for (int j = 1; j < (step256 >> 8); j++) {
            int index = (((i * step256) >> 8) + j) % (SCOPE_ADC_BUFFER_POINTS);
            if (info->buffer[i].ch0_min > pos[index].ch0) {
                info->buffer[i].ch0_min = pos[index].ch0;
            }
            if (info->buffer[i].ch0_max < pos[index].ch0) {
                info->buffer[i].ch0_max = pos[index].ch0;
            }
            if (info->buffer[i].ch1_min > pos[index].ch1) {
                info->buffer[i].ch1_min = pos[index].ch1;
            }
            if (info->buffer[i].ch1_max < pos[index].ch1) {
                info->buffer[i].ch1_max = pos[index].ch1;
            }
        }
    }
}

void *scope_snap_buffer_get_dma_target(void)
{ 
    return g_scope_dma_points[g_scope_snap_buffer_index];
}

void scope_snap_buffer_trunk_data(scope_snap_info *info)
{
    if (info->length > HTTP_FS_IV_NUM) {
        info->length = HTTP_FS_IV_NUM;
    }
    info->offset = HTTP_FS_IV_NUM - info->length;
}

void scope_snap_buffer_read(scope_snap_info *info)
{
    if (dma_channel_hw_addr(g_scope_cfg.dma_chan_adc_data)->transfer_count == 0) {
        g_scope_snap_buffer_ready = true;
        g_scope_snap_buffer_index = !g_scope_snap_buffer_index;
        scope_hw_init_dma_adc_to_buffer();
    }
    scope_snap_buffer_copy_data(info);
    scope_snap_buffer_move_data(info);
    scope_helper_buffer_cali_data(info);
    scope_snap_buffer_trunk_data(info);
}

void scope_snap_buffer_start(void)
{
    g_scope_snap_buffer_index = PING;
    g_scope_snap_buffer_ready = false;
}

static int scope_snap_buffer_get_rate_by_div(uint32_t time_div_us)
{ 
    int rate = SCOPE_ADC_BUFFER_POINTS * 1000000.0 / (time_div_us * SCOPE_TIME_DIV_NUM * 2);

    if (rate > SCOPE_MAX_SAMPLE_RATE) {
        rate = SCOPE_MAX_SAMPLE_RATE;
    }
    return rate;
}

static bool scope_snap_buffer_div_valid(uint32_t time_div_us)
{
    int rate = scope_snap_buffer_get_rate_by_div(time_div_us);

    if (rate <= SCOPE_MIN_SAMPLE_RATE) {
        return false;
    }
    return true;
}

void scope_snap_buffer_scale(int offset, int step)
{
    uint32_t div;
    int index = scope_helper_value_to_index_round_up(g_scope_cfg.time_div_us);

    if (step < 0) {
        index++;
        g_scope_cfg.time_div_us = scope_helper_index_to_value(index);
    } else {
        index--;
        g_scope_cfg.time_div_us = scope_helper_index_to_value(index);
    }
    scope_helper_correct_time_div();
    g_scope_cfg.rate = scope_snap_buffer_get_rate_by_div(g_scope_cfg.time_div_us);
    scope_hw_set_sample_rate(g_scope_cfg.rate);
}
